<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
<script>
//Promise 的三种状态  (满足要求 -> Promise的状态)
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class AjPromise {
  constructor(fn) {
    this.state = PENDING;
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    //成功态回调
    const resolve = value => {
      // 使用macro-task机制(setTimeout), 确保onFulfilled异步执行
      setTimeout(() => {
        if (this.state === PENDING) {
          // pending(等待态)迁移至 fulfilled(执行态), 保证调用次数不超过一次
          this.state = FULFILLED;
          this.value = value;
          this.onFulfilledCallbacks.forEach(cb => {
            this.value = cb(this.value);
          });
        }
      });
    };
    //拒绝态回调
    const reject = reason => {
      // 使用macro-task机制(setTimeout), 确保onRejected异步执行
      setTimeout(() => {
        if (this.state === PENDING) {
          // pending(等待态)迁移至 fulfilled(拒绝态),保证调用次数不超过一次。
          this.state = REJECTED;
          this.reason = reason;
          this.onRejectedCallbacks.forEach(cb => {
            cb(this.reason);
          });
        }
      });
    };
    try {
      //执行promise
      fn(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onFulfilled, onRejected) {
    typeof onFulfilled === 'function' && this.onFulfilledCallbacks.push(onFulfilled);
    typeof onRejected === 'function' && this.onRejectedCallbacks.push(onRejected);
    // TODO: 重点: 返回this支持then 方法可以被同一个 promise 调用多次
    return this;
  }
}

new AjPromise((resolve, reject) => {
  // throw new Error(1);
  setTimeout(() => {
    resolve(2);
    console.log('sync');
  }, 2000);
}).then(res => {
  console.log(res);
  return res + 1;
}).then(res => {
  console.log(res);
})
// .then(null, (err) => {
//   console.log(err);
// })

// sync
//  2
//  3
</script>
</body>
</html>